/*
 * Created on Nov 21, 2003
 */
package com.unash.asn;

/**
 * @author Ashwini Gokhale
 */
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import com.unash.util.AsnUtil;
import com.unash.util.ByteArray;
import com.unash.util.Consts;

public class BerDecoder implements Consts {
	/**
	 * This function reads the number of bytes specified by length param and
	 * creates an AsnInteger object. It is assumed that if length = 0 then
	 * contents has indefinite length and thus contents are read until
	 * end-of-contents octet is found.
	 * 
	 * @param bs
	 *            InputStream
	 * @param length
	 *            no of bytes in the contents octets
	 * @return AsnInteger object
	 */
	public static AsnInteger decodeInteger(InputStream bs, int length)
			throws Exception {
		byte[] intValue = null;

		// Indefinite Length
		if (ZERO == length) {
			int count = ZERO;
			byte prevByte = (byte) MAGIC_NUMBER;
			byte currentByte = (byte) MAGIC_NUMBER;
			intValue = new byte[INT_SIZE + END_OF_CONTENTS_SIZE];
			while (true) {
				if (INT_SIZE + END_OF_CONTENTS_SIZE <= count)
					throw new Exception("Integer value more than 4 bytes "
							+ "is not supported");

				currentByte = AsnUtil.readByte(bs);
				intValue[count++] = currentByte;
				if ((ZERO == currentByte) && (ZERO == prevByte))
					break;

				prevByte = currentByte;
			}// while(true)
			count--;
			intValue = AsnUtil.getLeadingBytes(intValue, count
					- END_OF_CONTENTS_SIZE);
		} else {
			// Definite Length
			intValue = AsnUtil.readNBytes(bs, length);
		}// else
		ByteArray valueBA = new ByteArray(intValue);
		return new AsnInteger(Integer.parseInt(valueBA.toString(), BINARY));
	}// decodeInteger(InputStream bs,int length)

	/**
	 * This function read and decode first byte and return AsnBoolean object.
	 * 
	 * @param bs
	 * @return AsnBoolean object
	 * @throws Exception
	 */
	public static AsnBoolean decodeBoolean(InputStream bs) throws Exception {
		AsnBoolean result = null;
		byte booleanValue = AsnUtil.readByte(bs);
		if (ZERO == booleanValue)
			result = new AsnBoolean(false);
		else
			result = new AsnBoolean(true);

		return result;
	}// decodeBoolean(InputStream bs)

	/**
	 * This function reads the no of bytes specified by length. It will decode
	 * the byte stream and create the AsnBitString object. It is assumed that if
	 * length = 0 then contents has indefinite length and thus contents are read
	 * until end-of-contents octet is found.
	 * 
	 * @param bs
	 *            Ber Encoded Stream
	 * @param length
	 * @return AsnBitString Object
	 * @throws Exception
	 */

	public static AsnBitString decodeBitString(InputStream bs, int length)
			throws Exception {
		AsnBitString result = null;
		byte[] bitStr = null;
		// Indefinite length
		if (ZERO == length) {
			List<Byte> bitList = new ArrayList<Byte>();
			byte prevByte = MAGIC_NUMBER;
			byte currentByte = MAGIC_NUMBER;
			while (true) {
				currentByte = AsnUtil.readByte(bs);
				if ((ZERO == currentByte) && (ZERO == prevByte))
					break;
				bitList.add(new Byte(currentByte));
				prevByte = currentByte;
			}// while(true)

			Byte[] bitStrByte = bitList.toArray(new Byte[] {});
			bitStr = new byte[bitStrByte.length];
			for (int i = ZERO; i < bitStrByte.length; i++) {
				bitStr[i] = bitStrByte[i].byteValue();
			}// for
		} else {
			// Definite Length
			bitStr = AsnUtil.readNBytes(bs, length);
		}// else
		// manipulate the size of BitString [using the first byte]
		int bitStringLength = (BYTE_SIZE * (length - ONE)) - (int) bitStr[ZERO];
		bitStr = AsnUtil.getTrailingBytes(bitStr, (bitStr.length - ONE));
		result = new AsnBitString(bitStr, bitStringLength);
		return result;
	}// decodeBitString(InputStream bs)

	/**
	 * This function reads the no of bytes specified by length. It will decode
	 * the byte stream and create the AsnOctetString object. It is assumed that
	 * if length = 0 then contents has indefinite length and thus contents are
	 * read until end-of-contents octet is found.
	 * 
	 * @param bs
	 *            Ber Encoded Stream
	 * @param length
	 * @return AsnBitString Object
	 * @throws Exception
	 */
	public static AsnOctetString decodeOctetString(InputStream bs, int length)
			throws Exception {
		AsnOctetString result = null;
		byte[] octetStr = null;
		// Indefinite length
		if (ZERO == length) {
			List<Byte> octetList = new ArrayList<Byte>();
			byte prevByte = MAGIC_NUMBER;
			byte currentByte = MAGIC_NUMBER;
			while (true) {
				currentByte = AsnUtil.readByte(bs);
				if ((ZERO == currentByte) && (ZERO == prevByte))
					break;
				octetList.add(new Byte(currentByte));
				prevByte = currentByte;
			}// while(true)
			Byte[] octetStrByte = octetList.toArray(new Byte[] {});
			octetStr = new byte[octetStrByte.length];
			for (int i = ZERO; i < octetStrByte.length; i++) {
				octetStr[i] = octetStrByte[i].byteValue();
			}// for(int i=0;i<octetStrByte.length;i++)
		} else {
			// Definite Length
			octetStr = new byte[length];
			octetStr = AsnUtil.readNBytes(bs, length);
		}// else
		result = new AsnOctetString(octetStr);
		return result;
	}// decodeOctetString(InputStream bs,int length)

	/**
	 * This function reads no of bytes specified by length field. It will decode
	 * the String and create AsnVisibleObject.
	 * 
	 * @param bs
	 *            Ber Encoded Stream
	 * @param length
	 * @return AsnVisibleObject
	 * @throws Exception
	 */
	public static AsnVisibleString decodeVisibleString(InputStream bs,
			int length) throws Exception {
		// Indefinite length
		byte[] vStr = null;
		if (ZERO == length) {
			List<Byte> visibleStrList = new ArrayList<Byte>();
			byte prevByte = MAGIC_NUMBER;
			byte currentByte = MAGIC_NUMBER;
			while (true) {
				currentByte = AsnUtil.readByte(bs);
				if ((ZERO == currentByte) && (ZERO == prevByte))
					break;
				visibleStrList.add(new Byte(currentByte));
				prevByte = currentByte;
			}// while(true)
			Byte[] visibleStrByte = visibleStrList.toArray(new Byte[] {});
			vStr = new byte[visibleStrByte.length];
			for (int i = ZERO; i < visibleStrByte.length; i++) {
				vStr[i] = visibleStrByte[i].byteValue();
			}// for(int i=0;i<octetStrByte.length;i++)
		} else
			vStr = AsnUtil.readNBytes(bs, length);

		AsnVisibleString result = new AsnVisibleString(new String(vStr, ZERO,
				vStr.length, VISIBLE_STRING_CHARSET));
		return result;
	}// AsnVisibleString

	public static AsnObjectIdentifier decodeObjectIdentifier(InputStream bs,
			int length) throws Exception {
		AsnObjectIdentifier result = null;
		AsnUtil.readNBytes(bs, length);
		return result;
	}// decodeObjectIdentifier

}// BerDecoder
